package compiler.frames;

import compiler.abstree.*;
import compiler.abstree.tree.*;
import compiler.semanal.*;
import compiler.semanal.type.SemType;

public class FrmEvaluator implements AbsVisitor {
	
	private AbsTree subprog;	
	
	private void insertStandardFunctionsFrames() {
		String[] stdFun = {"putch", "putint", "getch", "getint", "ord", "chr", "free"};
		
		for(String fun:stdFun) {
			AbsDecl decl=SemTable.fnd(fun);
			
			if(decl instanceof AbsFunDecl)
				this.visit((AbsFunDecl)decl);
			else
				this.visit((AbsProcDecl)decl);
		}
	}
	
	public void visit(AbsFunDecl acceptor) {
		FrmFrame frame = new FrmFrame(acceptor, SemDesc.getScope(acceptor));
		for (AbsDecl decl : acceptor.pars.decls) {
			if (decl instanceof AbsVarDecl) {
				AbsVarDecl varDecl = (AbsVarDecl)decl;
				FrmArgAccess access = new FrmArgAccess(varDecl, frame);
				FrmDesc.setAccess(varDecl, access);
			}
		}		
		for (AbsDecl decl : acceptor.decls.decls) {
			if (decl instanceof AbsVarDecl) {
				AbsVarDecl varDecl = (AbsVarDecl)decl;
				FrmLocAccess access = new FrmLocAccess(varDecl, frame);
				frame.locVars.add(access);
				FrmDesc.setAccess(varDecl, access);
			}
			decl.accept(this);
		}
		FrmDesc.setFrame(acceptor, frame);
		this.subprog=acceptor;
		acceptor.stmt.accept(this);
	}
	
	public void visit(AbsProgram acceptor) {
		this.insertStandardFunctionsFrames();
		this.subprog=acceptor;
		
		FrmFrame frame = new FrmFrame(acceptor, 0);
		for (AbsDecl decl : acceptor.decls.decls) {
			if (decl instanceof AbsVarDecl) {
				AbsVarDecl varDecl = (AbsVarDecl)decl;
				FrmVarAccess access = new FrmVarAccess(varDecl);
				FrmDesc.setAccess(varDecl, access);
			}
			decl.accept(this);
		}
		FrmDesc.setFrame(acceptor, frame);
	}
	
	public void visit(AbsProcDecl acceptor) {
		FrmFrame frame = new FrmFrame(acceptor, SemDesc.getScope(acceptor));
		for (AbsDecl decl : acceptor.pars.decls) {
			if (decl instanceof AbsVarDecl) {
				AbsVarDecl varDecl = (AbsVarDecl)decl;
				FrmArgAccess access = new FrmArgAccess(varDecl, frame);
				FrmDesc.setAccess(varDecl, access);
			}
		}
		for (AbsDecl decl : acceptor.decls.decls) {
			if (decl instanceof AbsVarDecl) {
				AbsVarDecl varDecl = (AbsVarDecl)decl;
				FrmLocAccess access = new FrmLocAccess(varDecl, frame);
				FrmDesc.setAccess(varDecl, access);
			}
			decl.accept(this);
		}
		FrmDesc.setFrame(acceptor, frame);
		this.subprog=acceptor;
		acceptor.stmt.accept(this);
	}
	
	public void visit(AbsRecordType acceptor) {
		int offset = 0;
		for (AbsDecl decl : acceptor.fields.decls)
			if (decl instanceof AbsVarDecl) {
				AbsVarDecl varDecl = (AbsVarDecl)decl;
				FrmCmpAccess access = new FrmCmpAccess(varDecl, offset);
				FrmDesc.setAccess(varDecl, access);
				offset = offset + SemDesc.getActualType(varDecl.type).size();
				decl.accept(this);
			}
	}
	
	public void visit(AbsTypeDecl acceptor) {
		acceptor.type.accept(this);
	}
	
	public void visit(AbsVarDecl acceptor) {
		acceptor.type.accept(this);
	}
	
	public void visit(AbsCallExpr acceptor) {
		FrmFrame frm=FrmDesc.getFrame(subprog);
		int size=4;
		
		for(AbsValExpr decl:acceptor.args.exprs)
			size += SemDesc.getActualType(decl).size();
		
		if(frm.sizeArgs<size)
			frm.sizeArgs=size;
	}

	@Override
	public void visit(AbsAlloc acceptor) {
		acceptor.type.accept(this);
	}

	@Override
	public void visit(AbsArrayType acceptor) {
		acceptor.hiBound.accept(this);
		acceptor.loBound.accept(this);
		acceptor.type.accept(this);
	}

	@Override
	public void visit(AbsAssignStmt acceptor) {
		acceptor.srcExpr.accept(this);
		acceptor.dstExpr.accept(this);
	}

	@Override
	public void visit(AbsBinExpr acceptor) {
		acceptor.fstExpr.accept(this);
		acceptor.sndExpr.accept(this);
	}

	@Override
	public void visit(AbsBlockStmt acceptor) {
		for(AbsStmt stmt:acceptor.stmts.stmts)
			stmt.accept(this);
	}

	@Override
	public void visit(AbsExprStmt acceptor) {
		acceptor.expr.accept(this);
	}

	@Override
	public void visit(AbsForStmt acceptor) {
		acceptor.hiBound.accept(this);
		acceptor.loBound.accept(this);
		acceptor.name.accept(this);
		acceptor.stmt.accept(this);
	}

	@Override
	public void visit(AbsIfStmt acceptor) {
		acceptor.cond.accept(this);
		acceptor.thenStmt.accept(this);
		acceptor.elseStmt.accept(this);
	}
	
	@Override
	public void visit(AbsPointerType acceptor) {
		acceptor.type.accept(this);
	}

	@Override
	public void visit(AbsStmts acceptor) {
		for(AbsStmt stmt:acceptor.stmts)
			stmt.accept(this);
	}

	@Override
	public void visit(AbsUnExpr acceptor) {
		acceptor.expr.accept(this);
	}

	@Override
	public void visit(AbsValExprs acceptor) {
		for(AbsValExpr expr:acceptor.exprs)
			expr.accept(this);
	}

	@Override
	public void visit(AbsWhileStmt acceptor) {
		acceptor.cond.accept(this);
		acceptor.stmt.accept(this);
	}

	@Override
	public void visit(AbsAtomConst acceptor) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void visit(AbsAtomType acceptor) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void visit(AbsConstDecl acceptor) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void visit(AbsDeclName acceptor) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void visit(AbsDecls acceptor) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void visit(AbsNilConst acceptor) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void visit(AbsTypeName acceptor) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void visit(AbsValName acceptor) {
		// TODO Auto-generated method stub
		
	}
	
}
